package com.iwdnb.gkgz.application.service.impl;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.gkgz.application.model.request.StockSignalRequest;
import com.iwdnb.gkgz.application.service.StockService;
import com.iwdnb.gkgz.application.service.StockSignalService;
import com.iwdnb.gkgz.application.utils.ExecutorUtils;
import com.iwdnb.gkgz.common.enums.IntersectTypeEnums;
import com.iwdnb.gkgz.common.enums.TrendTypeEnums;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockRangeScopeDTO;
import com.iwdnb.gkgz.common.model.dto.StockSignalDTO;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.StockBlackListUtils;
import com.iwdnb.gkgz.common.utils.StockTrendUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.wwzy.dao.MetaDataDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import static com.iwdnb.gkgz.common.constant.GkgzConstants.OBJECT_CODE_STOCK_SIGNAL;
import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;

@Service
@Slf4j
public class StockSignalServiceImpl implements StockSignalService {
    private Executor executor = ExecutorUtils.getExecutor();

    @Autowired
    protected StockService stockService;
    @Autowired
    protected StockDayDataRepository stockDayDataRepository;
    @Autowired
    private MetaDataDAO metaDataDAO;

    @Override
    public void initStockSignals(StockSignalRequest request) {
        List<Stock> stockList = stockService.queryStockList();
        String codeStr = request.getCodes();
        List<String> codes = null;
        if (StringUtils.isNotBlank(codeStr)) {
            codes = Lists.newArrayList(codeStr.split(","));
        } else {
            codes = stockList.stream().map(stock ->  stock.getCode()).collect(Collectors.toList());
            codes = StockBlackListUtils.getCanBuyCodeList(codes);
            int end = request.getEnd() > codes.size() ? codes.size() : request.getEnd();
            Collections.sort(codes);
            codes = codes.subList(request.getBegin(), end);
        }
        for (int i = 0; i < codes.size(); i++) {
            String code = codes.get(i);
            int finalIndex = i;
            executor.execute(() -> {
                initStockSignal(code);
                log.info("{}-{}-initStockSignal finished", code, finalIndex);
            });

        }
    }

    public void initStockSignal(String code) {
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAll(code);
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return;
        }
        int longPeriod = 60;
        int shortPeriod = 30;
        int startIndex = longPeriod * 2 + 1;
        MovingAverage.calculateEMA(stockDayDataList, longPeriod);
        MovingAverage.calculateEMA(stockDayDataList, shortPeriod);
        for (int i = 0; i < stockDayDataList.size(); i++) {
            if (i < startIndex) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            if (!StockUtils.isFirstLimitUp(data, stockDayDataList, 20)) {
                continue;
            }

            buildStockSignal(code, data, stockDayDataList, longPeriod, shortPeriod);
        }
    }

    private void buildStockSignal(String code, StockDayData data, List<StockDayData> stockDayDataList, int longPeriod,
        int shortPeriod) {
        String date = data.getDate();
        BigDecimal closePrice = data.getClosePrice();
        BigDecimal longAvg = getAveragePrice(data, longPeriod);
        BigDecimal shortAvg = getAveragePrice(data, shortPeriod);
        List<StockDayData> twoMonthDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date,
            45);
        //最近2个月涨跌停板数量
        int twoMonthLimitUpNum = 0;
        int twoMonthLimitDownNum = 0;
        for (StockDayData stockDayData : twoMonthDayDatas) {
            if (StockUtils.isLimitUp(stockDayData)) {
                twoMonthLimitUpNum++;
            } else if (StockUtils.isLimitDown(stockDayData)) {
                twoMonthLimitDownNum++;
            }
        }
        //取2个月前(45天),1个月前(22天)，10天前的成交量
        int twoMonthTradeNum = 0;
        int oneMonthTradeNum = 0;
        int tenDayTradeNum = 0;
        for (int i = 0; i < 45; i++) {
            StockDayData stockDayData = twoMonthDayDatas.get(i);
            twoMonthTradeNum += stockDayData.getTradeNum();
            if (i >= 23) {
                oneMonthTradeNum += stockDayData.getTradeNum();
            }
            if (i >= 35) {
                tenDayTradeNum += stockDayData.getTradeNum();
            }
        }
        twoMonthTradeNum = twoMonthTradeNum / 45;
        oneMonthTradeNum = oneMonthTradeNum / 22;
        tenDayTradeNum = tenDayTradeNum / 10;
        int tradeNum = data.getTradeNum();
        //未来10天的成交量对比
        List<StockDayData> afterTenDayDatas = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date,
            10);
        String afterTradeNumList = "";
        int shrinkageCount = 0;
        int beforeTradeNum = afterTenDayDatas.get(0).getTradeNum();
        //极致缩量到四分之一
        AtomicBoolean shrinkageOneFourthFlag = new AtomicBoolean(false);
        int size = afterTenDayDatas.size() == 10 ? 10 : afterTenDayDatas.size();
        for (int i = 1; i < size; i++) {
            StockDayData afterDayData = afterTenDayDatas.get(i);
            afterTradeNumList += afterDayData.getTradeNum() + "-";
            if (afterDayData.getTradeNum() < beforeTradeNum) {
                shrinkageCount++;
                //极致缩量到四分之一
                if (shrinkageCount * 4 < tradeNum) {
                    shrinkageOneFourthFlag.compareAndSet(false, true);
                }
            }
        }
        afterTradeNumList = StringUtils.removeEnd(afterTradeNumList, "-");
        //取2个月前(45天),1个月前(22天)，10天前的数据
        StockDayData twoMonthData = twoMonthDayDatas.get(0);
        StockDayData oneMonthData = twoMonthDayDatas.get(22);
        StockDayData tenDayData = twoMonthDayDatas.get(35);
        //短周期
        BigDecimal twoMonthShortAvg = getAveragePrice(twoMonthData, shortPeriod);
        BigDecimal twoMonthShortRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthShortAvg,
            twoMonthData.getClosePrice());
        BigDecimal oneMonthShortAvg = getAveragePrice(oneMonthData, shortPeriod);
        BigDecimal oneMonthShortRate = BigDecimalUtils.subStractAndDividePrecent(oneMonthShortAvg,
            oneMonthData.getClosePrice());
        BigDecimal tenDayShortAvg = getAveragePrice(tenDayData, shortPeriod);
        BigDecimal tenDayShortRate = BigDecimalUtils.subStractAndDividePrecent(tenDayShortAvg,
            tenDayData.getClosePrice());
        //短均线趋势
        TrendTypeEnums shortTrend = TrendTypeEnums.getTrendType(twoMonthShortAvg, oneMonthShortAvg,
            tenDayShortAvg, shortAvg);
        //长周期
        BigDecimal twoMonthLongAvg = getAveragePrice(twoMonthData, longPeriod);
        BigDecimal twoMonthLongRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthLongAvg,
            twoMonthData.getClosePrice());
        BigDecimal oneMonthLongAvg = getAveragePrice(oneMonthData, longPeriod);
        BigDecimal oneMonthLongRate = BigDecimalUtils.subStractAndDividePrecent(oneMonthLongAvg,
            oneMonthData.getClosePrice());
        BigDecimal tenDayLongAvg = getAveragePrice(tenDayData, longPeriod);
        BigDecimal tenDayLongRate = BigDecimalUtils.subStractAndDividePrecent(tenDayLongAvg,
            tenDayData.getClosePrice());
        //长均线趋势
        TrendTypeEnums longTrend = TrendTypeEnums.getTrendType(twoMonthLongAvg, oneMonthLongAvg,
            tenDayLongAvg, longAvg);

        //长短均线交叉
        IntersectTypeEnums intersectTypeEnums = IntersectTypeEnums.getType(tenDayShortAvg, oneMonthShortAvg,
            tenDayShortAvg, twoMonthLongAvg, oneMonthLongAvg, tenDayShortAvg);

        //长周期与当前价格的距离
        BigDecimal longPeriodPriceDistance = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
            getAveragePrice(data, longPeriod));
        String longPeriodUpFlag = BigDecimalUtils.isGe(longPeriodPriceDistance, BigDecimal.ZERO) ? "y" : "n";
        //短周期与当前价格的距离
        BigDecimal shortPeriodPriceDistance = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
            getAveragePrice(data, shortPeriod));
        String shortPeriodUpFlag = BigDecimalUtils.isGe(shortPeriodPriceDistance, BigDecimal.ZERO) ? "y" : "n";

        //前两个月内的高低点
        String beginDate = DateUtil.formatDate(DateUtil.offsetDay(DateUtil.parseDate(date), -45));
        StockRangeScopeDTO rangeScopeDTO = StockUtils.getRangeScopeByDateRange(stockDayDataList, beginDate, date);
        String twoMonthHighDate = rangeScopeDTO.getHighDate();
        BigDecimal twoMonthHighPrice = rangeScopeDTO.getHighPrice();
        BigDecimal twoMonthHighRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthHighPrice,
            closePrice);
        String twoMonthLowDate = rangeScopeDTO.getLowDate();
        BigDecimal twoMonthLowPrice = rangeScopeDTO.getLowPrice();
        BigDecimal twoMonthLowRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthLowPrice, closePrice);
        String twoMonthHighBeforeFlag = DateUtil.parseDate(twoMonthLowDate).after(DateUtil.parseDate(twoMonthHighDate))
            ? "y" : "n";
        //未来两个月的高低点
        String endDate = DateUtil.formatDate(DateUtil.offsetDay(DateUtil.parseDate(date), 60));
        rangeScopeDTO = StockUtils.getRangeScopeByDateRange(stockDayDataList, date, endDate);
        String twoMonthLaterHighDate = rangeScopeDTO.getHighDate();
        BigDecimal twoMonthLaterHighPrice = rangeScopeDTO.getHighPrice();
        BigDecimal twoMonthLaterHighRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthLaterHighPrice,
            closePrice);
        String twoMonthLaterLowDate = rangeScopeDTO.getLowDate();
        BigDecimal twoMonthLaterLowPrice = rangeScopeDTO.getLowPrice();
        BigDecimal twoMonthLaterLowRate = BigDecimalUtils.subStractAndDividePrecent(twoMonthLaterLowPrice, closePrice);
        //是否盈利
        String winFlag = BigDecimalUtils.isGe(twoMonthLaterHighRate, new BigDecimal(30)) ? "y" : "n";

        StockSignalDTO stockSignal = new StockSignalDTO();
        stockSignal.setCode(code);
        stockSignal.setSignalDate(DateUtil.parseDate(date));
        stockSignal.setSignalCode(code + "_" + DateUtil.format(stockSignal.getSignalDate(), "yyyyMMdd"));
        stockSignal.setClosePrice(closePrice);
        stockSignal.setLongAvg(longAvg);
        stockSignal.setShortAvg(shortAvg);
        stockSignal.setTwoMonthTradeNum(twoMonthTradeNum);
        stockSignal.setOneMonthTradeNum(oneMonthTradeNum);
        stockSignal.setTenDayTradeNum(tenDayTradeNum);
        stockSignal.setTradeNum(tradeNum);
        stockSignal.setAfterTradeNumInfo(afterTradeNumList);
        stockSignal.setShrinkageOneFourthFlag(shrinkageOneFourthFlag.get() ? "y" : "n");
        stockSignal.setTwoMonthShortAvg(twoMonthShortAvg);
        stockSignal.setTwoMonthShortRate(twoMonthShortRate);
        stockSignal.setOneMonthShortAvg(oneMonthShortAvg);
        stockSignal.setOneMonthShortRate(oneMonthShortRate);
        stockSignal.setTenDayShortAvg(tenDayShortAvg);
        stockSignal.setTenDayShortRate(tenDayShortRate);
        stockSignal.setTwoMonthLongAvg(twoMonthLongAvg);
        stockSignal.setTwoMonthLongRate(twoMonthLongRate);
        stockSignal.setOneMonthLongAvg(oneMonthLongAvg);
        stockSignal.setOneMonthLongRate(oneMonthLongRate);
        stockSignal.setTenDayLongAvg(tenDayLongAvg);
        stockSignal.setTenDayLongRate(tenDayLongRate);
        stockSignal.setLongPeriod(longPeriod);
        stockSignal.setShortPeriod(shortPeriod);
        stockSignal.setLongTrend(longTrend.getCode());
        stockSignal.setShortTrend(shortTrend.getCode());
        stockSignal.setIntersectType(intersectTypeEnums.getCode());
        stockSignal.setShortPeriodPriceDistance(shortPeriodPriceDistance);
        stockSignal.setLongPeriodPriceDistance(longPeriodPriceDistance);
        stockSignal.setShortPeriodUpFlag(shortPeriodUpFlag);
        stockSignal.setLongPeriodUpFlag(longPeriodUpFlag);
        stockSignal.setTwoMonthHighDate(twoMonthHighDate);
        stockSignal.setTwoMonthHighPrice(twoMonthHighPrice);
        stockSignal.setTwoMonthHighRate(twoMonthHighRate);
        stockSignal.setTwoMonthLowDate(twoMonthLowDate);
        stockSignal.setTwoMonthLowPrice(twoMonthLowPrice);
        stockSignal.setTwoMonthLowRate(twoMonthLowRate);
        stockSignal.setTwoMonthHighBeforeFlag(twoMonthHighBeforeFlag);
        stockSignal.setTwoMonthLaterHighDate(twoMonthLaterHighDate);
        stockSignal.setTwoMonthLaterHighPrice(twoMonthLaterHighPrice);
        stockSignal.setTwoMonthLaterHighRate(twoMonthLaterHighRate);
        stockSignal.setTwoMonthLaterLowDate(twoMonthLaterLowDate);
        stockSignal.setTwoMonthLaterLowPrice(twoMonthLaterLowPrice);
        stockSignal.setTwoMonthLaterLowRate(twoMonthLaterLowRate);
        stockSignal.setTwoMonthLimitUpNum(twoMonthLimitUpNum);
        stockSignal.setTwoMonthLimitDownNum(twoMonthLimitDownNum);
        stockSignal.setWinFlag(winFlag);
        metaDataDAO.insert(OBJECT_CODE_STOCK_SIGNAL, stockSignal);
        log.info("{}-{}涨停数据,winFlag:{},highRate:{},lowRate:{}", code, date, winFlag, twoMonthLaterHighRate,
            twoMonthLaterLowRate);

    }
}
